package org.byd.dmsja.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class SwdNodeUtil {
    public static <T> SwdNode<T> swdBuildNode(CNodeBuildOption<T> option) {
        Method.IFunc1<T, Object> fnGetId = (T xRecord) -> {
            return option.swdIdSelector.apply(xRecord);
        };

        Method.IFunc1<T, Object> fnGetParentId = (T xRecord) -> {
            return option.swdParentIdSelector.apply(xRecord);
        };

        Method.IPredicate1<T> fnHasParent = (T xRecord) -> {
            T parentRecord = option.swdDataList.stream()
                    .filter(x -> Objects.equals(fnGetId.apply(x), fnGetParentId.apply(xRecord)))
                    .findFirst()
                    .orElse(null);
            return parentRecord != null;
        };

        Method.IFunc0<List<T>> fnGetTopList = () -> {
            return option.swdDataList.stream().filter(x -> !fnHasParent.apply(x)).collect(Collectors.toList());
        };

        Method.IFunc1<T, List<T>> fnGetChildList = (T xRecord) -> {
            return option.swdDataList.stream()
                    .filter(x -> Objects.equals(fnGetParentId.apply(x), fnGetId.apply(xRecord)))
                    .collect(Collectors.toList());
        };

        AtomicReference<Consumer<SwdNode<T>>> acCallback = new AtomicReference<>();
        acCallback.set((SwdNode<T> xCurrentNode) -> {
            List<T> tmpList = xCurrentNode.swdRecord == null ? fnGetTopList.apply() :
                    fnGetChildList.apply(xCurrentNode.swdRecord);
            tmpList.forEach(x -> {
                var xNode = new SwdNode<T>();
                xNode.swdRecord = x;
                xNode.swdParentNode = xCurrentNode;
                xNode.swdChildNodeList = new ArrayList<>();
                xCurrentNode.swdChildNodeList.add(xNode);
                acCallback.get().accept(xNode);
            });
        });

        var node = new SwdNode<T>();
        acCallback.get().accept(node);
        return node;
    }

    public static class CNodeBuildOption<T> {
        public List<T> swdDataList = new ArrayList<>();
        public Method.IFunc1<T, Object> swdIdSelector = (T record) -> {
            return this.getValueByName("id", record);
        };
        public Method.IFunc1<T, Object> swdParentIdSelector = (T record) -> {
            return this.getValueByName("pid", record);
        };


        private Object getValueByName(String name, T record) {
            Object ret = null;
            try {
                var fields = record.getClass().getFields();
                for (var field : fields) {
                    if (field.getName().equals(name)) {
                        ret = field.get(record);
                    }
                }
            } catch (IllegalAccessException e) {
                var dummy = "";
            }
            return ret;
        }
    }

}
